iT邦幫忙

2021 iThome 鐵人賽

DAY 15
0

嘿,今天又是實作環節!比起單純的範例,這個環節是希望把我們聊的模組/功能實際應用,讓大家看看應用在實際的程式上,這些函式/屬性會怎麼被運用。

雖然這個實例可能不一定大家都會嘗試,但如果可以,希望大家都能有個機會自己嘗試發想一個題目。如我們一開始講得一樣,有目的的學習,才會讓你在了解這些知識的時候更有方向、更有印象。

開始前一樣來看一下需求清單,今天要利用前面兩天的時間控制與鏡頭控制,來實現下面我們畫粗體的功能。

  • 建立方釘和顏色釘
  • 方釘和顏色釘會是固定的
  • 建立圓球
  • 圓球需要自由落體,受到重力影響
  • 圓球要能和方釘碰撞產生彈跳
  • 圓球和顏色釘碰撞的時候要偵測到碰撞並變色
  • 鏡頭移動,上帶到下
  • 在開始前,畫面要是靜止的,直到我們發出Signal
  • 滑鼠和球體互動 (追加功能)

啊,如果讀者比對我們先前的清單會發現筆者多劃掉了下面這一項。

  • 在開始前,畫面要是靜止的,直到我們發出Signal

因為這項其實在我們有 runner button 的部分的當下就已經完成了,所以我先把它劃掉了。

  • 鏡頭移動,上帶到下

今天預計要實作得這項外,我們會根據原始影片再加上一項:

  • 當球與顏色釘靠近一定距離,減慢時間並聚光在球上

為了最後的效果,筆者其實加了比上面列的東西更多,但不會刻意去提,畢竟主角是 Matter.js ,只會在有和 Matter.js 相關的功能上著墨多些,若對實作效果,可以另外留言詢問。

那麼事不宜遲,我們直接開始今天的部分。

今天的Demo
今天的Demo原始碼
https://ithelp.ithome.com.tw/upload/images/20210930/20142057i4kHE7FJaM.png

今天 Demo 的原始碼有點進去的讀者應該會發現我拆成資料夾了,畢竟整個檔案內容越來越多,把所有 js 擠進去會讓後續整理或修改麻煩得多。目前筆者是 By feature 做分檔,重點的範例碼還是會複製出來,大家不用擔心。

首先我們看到視角控制的部分,邏輯上是要跟著鏡頭走,所以要做在 frameUpdate 的函式裡。

function frameUpdated()
{
    var cameraSpeedByFrame = 1.5 * isZoomIn? 0.3 : 1;
    Bounds.translate(render.bounds, {x:0,y:1.5});
    checkClosedToAnyColorBlock(blockList, mainBall);
}

這裡面的 cameraSpeedByFrame 就是鏡頭每個 frame 移動的 y 座標距離,也就是移動速度,isZoomIn 會是我們稍後提到判斷是否目前是球靠近顏色釘、時間減緩流逝的狀態。當時間減緩流逝的時候,鏡頭的移動距離也要乘上對應的倍率,才不會發生鏡頭過於迅速的往下的問題。

這裡我們用的是 Day13 提到的 Bounds 模組裡的 translate,來以增量平移 bounds 的座標,我們平移的對象就是我們的 render.bounds ,也就是直接影響我們畫面顯示的 bounds。

到這行為止,其實每秒就已經會讓鏡頭垂直向下了,可以注意到我們 x 不要變動,這是刻意的,在原本的需求裡面鏡頭就只會垂直移動。

下一行的 checkClosedToAnyColorBlock 其實就是來判斷是否進入靠近距離的部分,距離靠近,就要觸發減緩時間流逝與聚光燈效果。

function checkClosedToAnyColorBlock(blockList,mainBall)
{
    for(var i in blockList)
    {
        if(blockList[i])
        {
            var label = blockList[i].label;
            if(label == "GoldBlock" || label == "RainbowBlock")
            {
                if(getDistanceWithPoints({x:blockList[i].position .x, y:blockList[i].position .y},{x:mainBall.position .x,y:mainBall.position .y}) < zoomInDistance)
                {
                    triggerZoomIn();
                    isZoomIn = true;
                    return;
                }
            }
        }
    }
    isZoomIn = false;
    triggerZoomOut();
}

這邊是我們判斷距離的邏輯,會針對現存所有釘子做距離判斷,只要球和任一根顏色釘距離接近到定義距離內的時候,就會觸發 ZoomIn,並改變當下 isZoomIn狀態。如果沒有靠近任何一顆顏色釘,則解除 ZoomIn 狀態,回到正常狀態。

function triggerZoomIn()
{
    engine.timing.timeScale = 0.3;
    var mainBallX = mainBall.position.x;
    var mainBallY = mainBall.position.y - render.bounds.min.y;
    var backgroundString = "radial-gradient(10px 10px at " + mainBallX + "px " + mainBallY + "px, transparent 0, transparent 60px, rgba(0, 0, 0, 0.5) 65px)";
    var style = document.getElementById("overlayDiv").style;
    style.background = backgroundString;
    style.display = "block";
}

function triggerZoomOut()
{
    engine.timing.timeScale = 1;
    var style = document.getElementById("overlayDiv").style;
    style.display = "none";
}

這兩段就是處理緩慢時間流逝或復原時間流逝的部分,可以看到如果要緩慢的話我們會把時間流逝速度變成 0.3 倍,也就是我們上面同步對鏡頭做緩慢的倍率。要復原的話,就是把鏡頭速度變回 1 倍,時間流逝就會正常。

這邊稍稍提一下聚光燈的實作,是用一個 div 蓋在 canvas 上,透過 position : absolute 做到相疊,且插入 div 順序要在插入 canvas 之後。至於聚光燈的 css,相信 google 就會有各種實作了。最後就是把聚光燈的位置透過變數組成字串,在安到上層的 div style上,就能達成聚光在球上的效果。

要注意的是聚光燈的位置要扣掉鏡頭的 Y 軸向位移,否則會發現怎麼鏡頭一直在球的下方,可以看到我們 mainBallY 有減去 render.bounds.min.y ,也就是我們目前 render.bounds 的上方 y 邊界座標。

到這裡為止,除了一些美術,我們已經完全還原了當初我們的目標,也就是那個影片的效果。其實這個實作也算告一段落。但在影片中,球的落下是不可控的,我們後面有段加碼,也就是剩下的最後一個需求:讓滑鼠/點擊行為能和畫面互動,讓我們來控制/影響球的落下。

明天,讓我們來了解一下 Matter.js 裡面滑鼠怎麼和物體產生互動。


上一篇
Day14. 時光時光慢些吧,讓世界慢下來 - TimeScale
下一篇
Day16. 老鼠,老虎傻傻分不清楚?- Mouse(上)
系列文
在JS的世界碰碰撞撞乒乒乓乓!30天一起玩Matter.js!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言